package top.luckyluxcis;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.mime.FileBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import top.luckyluxcis.util.JsonUtil;

import java.io.File;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author Zhuang
 * @since 2023/9/20
 */
@SuppressWarnings("unused")
public class RollApi {
    private static RollApi API;
    private static final String DEFAULT_API_URL = "https://www.mxnzp.com/api";
    private static final RequestConfig DEFAULT_REQUEST_CONFIG = RequestConfig.custom()
            .setConnectionRequestTimeout(60, TimeUnit.SECONDS)
            .setResponseTimeout(60, TimeUnit.SECONDS)
            .build();
    private final String baseApiUrl;
    private final String appId;
    private final String appSecret;

    public RollApi(String baseApiUrl, String appId, String appSecret) {
        this.baseApiUrl = baseApiUrl;
        this.appId = appId;
        this.appSecret = appSecret;
    }

    public static RollApi getInstance() {
        return Optional.ofNullable(API)
                .orElseThrow(() -> new RuntimeException("RollApi未初始化"));
    }

    public static void init(String appId, String appSecret) {
        init(DEFAULT_API_URL, appId, appSecret);
    }

    public static void init(String baseApiUrl, String appId, String appSecret) {
        String baseUrl = baseApiUrl;
        if (baseApiUrl.endsWith("/")) {
            baseUrl = baseApiUrl.substring(0, baseApiUrl.length() - 1);
        }
        API = new RollApi(baseUrl, appId, appSecret);
    }

    public <T> T get(String apiPath, Map<String, Object> paramMap, Type... actualTypeArguments) {
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(60, TimeUnit.SECONDS)
                .setResponseTimeout(60, TimeUnit.SECONDS)
                .build();
        HttpGet httpGet = new HttpGet(baseApiUrl + apiPath + paramMap2ParamString(paramMap));
        RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).build();
        httpGet.setConfig(requestConfig);
        return execute(httpGet, actualTypeArguments);
    }

    public <T> T post(String apiPath, Map<String, Object> paramMap, Type... actualTypeArguments) {
        HttpPost httpPost = new HttpPost(baseApiUrl + apiPath + paramMap2ParamString(paramMap));
        RequestConfig requestConfig = RequestConfig.copy(DEFAULT_REQUEST_CONFIG).build();
        httpPost.setConfig(requestConfig);
        return execute(httpPost, actualTypeArguments);
    }

    public <T> T post(String apiPath, Map<String, Object> paramMap, File file, Type... actualTypeArguments) {
        HttpPost httpPost = new HttpPost(baseApiUrl + apiPath + paramMap2ParamString(paramMap));
        RequestConfig requestConfig = RequestConfig.copy(DEFAULT_REQUEST_CONFIG).build();
        httpPost.setConfig(requestConfig);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addPart("file", new FileBody(file));
        httpPost.setEntity(builder.build());
        return execute(httpPost, actualTypeArguments);
    }

    private <T> T execute(HttpUriRequest request, Type... actualTypeArguments) {
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            return client.execute(request, response -> {
                if (response.getEntity() != null) {
                    String body = EntityUtils.toString(response.getEntity());

                    RollApiResult<T> result = JsonUtil.parseObject(body, RollApiResult.class, actualTypeArguments);
                    if (1 != result.getCode()) {
                        throw new RuntimeException(result.getMsg());
                    }
                    return result.getData();
                } else {
                    return null;
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private String paramMap2ParamString(Map<String, Object> param) {
        param.put("app_id", appId);
        param.put("app_secret", appSecret);
        StringBuilder p = new StringBuilder();
        param.forEach((key, value) -> {
            if (p.length() != 0) {
                p.append("&");
            } else {
                p.append("?");
            }
            Optional.ofNullable(value).ifPresent(v -> {
                p.append(key).append("=").append(v);
            });
        });
        return p.toString();
    }
}
